package pdd;

import com.alibaba.fastjson.JSONObject;
import pdd.exceptions.PddExceptionCodeEnum;
import pdd.internal.util.CodecUtil;
import pdd.exceptions.PddException;
import pdd.internal.util.HttpUtil;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Created by gtx on 2018/6/11.
 * 拼多多ｃｌｉｅｎｔ默认实现
 *
 * @author gtx
 */
public class DefaultPddClient implements PddClient {
    /**
     * 所有的请求和响应数据编码皆为utf-8格式
     */
    public static final String CHARSET_UTF8 = "UTF-8";

    /**
     * api 调用地址
     */
    private String serverUrl;
    /**
     * token
     */
    private String accessToken;
    /**
     * 链接超时时间 ms
     */
    private int connectTimeout;
    /**
     * 读取资源超时时间 ms
     */
    private int readTimeout;
    /**
     * 应用APPKETY
     */
    private String appKey;
    /**
     * 应用密钥
     */
    private String appSecret;

    public DefaultPddClient(String serverUrl, String accessToken, String appKey, String appSecret) {
        this.serverUrl = serverUrl;
        this.accessToken = accessToken;
        this.connectTimeout = 0;
        this.readTimeout = 0;
        this.appKey = appKey;
        this.appSecret = appSecret;
    }

    public DefaultPddClient(String serverUrl, String accessToken, int connectTimeout, int readTimeout, String appKey, String appSecret) {
        this.serverUrl = serverUrl;
        this.accessToken = accessToken;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.appKey = appKey;
        this.appSecret = appSecret;
    }

    /**
     * 执行请求
     * @param request 请求
     * @param <T> 返回结果类型
     */
    @Override
    public <T extends PddResponse> T excute(PddRequest<T> request) {
        Class<T> resClazz = request.getResponseClass();
        Map<String, String> params = request.getParams();
        //添加 client_id  access_token
        params.put("client_id",this.appKey);
        params.put("access_token",this.accessToken);

        try {
            String sign = this.sign(params,this.appSecret);
            params.put("sign",sign);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return returnErrResponse(resClazz, PddExceptionCodeEnum.UNSUPPORTED_ENCODING_ERROR);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return returnErrResponse(resClazz,PddExceptionCodeEnum.NOSUCH_ALGORITHM_ERROR);
        }

        try {
            String returnStr = HttpUtil.doPost(serverUrl, params, this.connectTimeout, this.readTimeout);
            if (request.getDataType().equals(PddRequest.JSON_RESPONSE)) {
                if (returnStr.contains("error_code")) {
                    JSONObject jsonObject = JSONObject.parseObject(returnStr);
                    String error_response = jsonObject.getString("error_response");
                    PddException pddException = JSONObject.parseObject(error_response).toJavaObject(PddException.class);
                    Object o = resClazz.newInstance();
                    Field code = resClazz.getSuperclass().getDeclaredField("success");
                    code.setAccessible(true);
                    code.set(o,false);
                    Field exception = resClazz.getSuperclass().getDeclaredField("exception");
                    exception.setAccessible(true);
                    exception.set(o,pddException);

                    return (T)o;// 为构造方法传递参数
                }
                JSONObject jsonObject = JSONObject.parseObject(returnStr);
                Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
                Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
                while (iterator.hasNext()){
                    Map.Entry<String, Object> next = iterator.next();
                    returnStr = jsonObject.getString(next.getKey());
                    break;
                }
                T t = JSONObject.parseObject(returnStr, resClazz);
                t.setSuccess(true);
                t.setData(returnStr);
                return t;
            }else {
                throw new IllegalArgumentException("熊孩子自己实现XML解析去");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return returnErrResponse(resClazz,PddExceptionCodeEnum.XZ_SYSTEM_ERROR);
        }

    }

    /**
     * 签名
     * @param param 参数
     * @param appSecret 密钥
     * @return 签名
     */
    private String sign(Map<String, String> param, String appSecret) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        StringBuilder sb = new StringBuilder(appSecret);

        ArrayList list = new ArrayList();

        for (Map.Entry<String,String> entry : param.entrySet()){
            list.add(entry.getKey());

        }
        Collections.sort(list);

        for(int i = 0; i < list.size(); ++i) {
            sb.append((String)list.get(i));
//            sb.append(URLEncoder.encode(param.get(list.get(i)), "UTF-8"));
            sb.append(param.get(list.get(i)));
        }

        sb.append(appSecret);
        String result = CodecUtil.md5(sb.toString());
        return result;
    }

    /**
     * 返回异常
     * @param pddExceptionCodeEnum 异常枚举
     * @param <T> 返回结果类型
     * @return responses
     */
    private <T extends PddResponse> T  returnErrResponse(Class<T> resClazz, PddExceptionCodeEnum pddExceptionCodeEnum) {
        Object o = null;
        try {
            PddException pddException =
                    new PddException(pddExceptionCodeEnum.getErrCode(),
                            pddExceptionCodeEnum.getErrMsg());
            o = resClazz.newInstance();
            Field code = resClazz.getSuperclass().getDeclaredField("success");
            code.setAccessible(true);
            code.set(o,false);
            Field exception = resClazz.getSuperclass().getDeclaredField("exception");
            exception.setAccessible(true);
            exception.set(o,pddException);

            return (T)o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("实例化返回结果失败");
    }

}
